iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0
Modern Web

跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset系列 第 13

[day12] YDKJS (Equality) : 不論任何 Equality,你應該「總是知道」型別。來做相等性 Cases 練習吧!

  • 分享至 

  • xImage
  •  

Case 1 : 你不應該 對任何 non-Primitive 做「任何比對」

[] == ![] // true WAT ???!

你不應該 對 non-Primitive 做「任何比對」,所以這題不講。 (X)


當然不會不講,不過其實邏輯上沒有意義。
所以真的可以不用講。 (?)

真實世界的邏輯上來說,沒有人會把 Array 去比對 一個 negative self Array。
(題外話,有人知道 negative self Array 到底要表示什麼嗎 XDDDD)

有這一題是因為,很多反對使用一般相等 ( == ) 的人都是用這個例子來說明 JavaScript 不應該使用 一般相等 ( == ),

但如果你把 == 視作 抽象等價性,non-Primitive 並沒有被抽象的意義。


舉個實際例子:

同樣的,如果你今天如果想要做的事情是,比對 兩個 Array 是不是一樣 ,應該考慮 line 8
A != B <===> ! ( A == B )

line 4 表示,你要比對一個 Array 去比對 一個 negative Array (??????)
其實你在做 Coercion , Coercion 之後才比對。

雖然兩種都不適合寫在 JavaScript ,但是其背後邏輯、意義完全不一樣,可以思考一下再往下看。


先思考 [] == ![]

  • line 4 : 做 ToBoolean 的 Coercion 運算,是查表,很顯然 [] 不是 falsy,ToBoolean([]) 回傳是 true 。


    ![] ==> !(true) => false

參考之前文章
[day08] YDKJS (Coercion:spec) : 回來讀 spec.  ToString(), ToNumber() , ToBoolean()
[day09] YDKJS (Coercion:spec) : Coercion 型別轉換 、 Boxing Wrappers

  • line 5 : non-Primitive 和 Primitive 比較相等,non-Primitive 會轉 ToPrimitive() 。

昨天有提到 Coercive Equality : string, number, boolean
[day11] YDKJS (Equality) : 一般相等 == 比較「值」 , 嚴格相等 === 比較 「型別和值」是錯的?

  • line 6 : 兩邊都是 Primitive,但型別不同,只要其中有一邊是 Number 就會傾向 ToNumber()。

再複習一下 ToNumber() :

  • 空字串變成 0
  • false -> 0 , true -> 1

reference:
[day08] YDKJS (Coercion:spec) : 回來讀 spec.  ToString(), ToNumber() , ToBoolean()

  • line 8 : 兩邊都是 Primitive 但型別不同 ,還是傾向 ToNumber()。
  • line 9 : 兩邊最後變成 0 == 0 ,型別一樣做嚴格相等 0 === 0 。
  • 回傳 true。

思考 [] != []

其實等於 ! ([] == [])
然後型別一樣( top level objects type),做嚴格相等。
!([] === [])

這邊之後的運算就不是 抽象等價性 (==) 的鍋,
原本 JavaScript 真的不適合來比對 non-Primitive,
你用 嚴格相等 === 還是不適合,因為 non-Primitive 做比較通常都有 reference share 的問題 。

  • line 15 : 嚴格相等 === 回傳 false ,還有 negative !(false) // true
  • 最後回傳 true。

Case 2: 你不應該使用 boolean 做任何比對

boolean 最好也不要用在 condition ,之前有提過 condition 要寫 非常具體 的如 array.length > 0。

假設你要寫的是 line 3雖然我不建議你這樣寫

  • line 3 : 查表不是 falsy ,所以是 true。
  • line 7 : 這邊就比對失敗,因為 non-Primitive 和 Primitive 比較相等,non-Primitive 會轉 ToPrimitive()。

[] == true // [] ToPrimitive()
也就是 "" == true
其中一邊是 boolean , boolean 還要再做 ToNumber() "" == 1
出現數字,另一邊空字串 ToNumber() 0 == 1
同數字型別,做 嚴格相等 === 。
回傳 false。

  • line 11 : 同上。

還是再三強調,如果這邊用 嚴格相等 (===),回傳也是 false 。
不過原因是因為型別不相等 回傳 false ,而不是比對相等失敗回傳 false。

Summary

  1. 避免用 抽象等價性 (==)0(數字 0)、 ""(空字串) " "(可以透過 Coercion 轉換成等效空字串的任何字串)。
  2. 你不應該 對任何 non-Primitive 做「任何比對」( == or ===)。
  3. 抽象等價性 (==) 不要用 == false or == true ,會觸發多次 Coercion 。
  • 如果你一定要比對 型別是 boolean 且值 true or false (=== true\false),才考慮不會處發 Coercion 的嚴格相等 (===) ,否則你得到 false 多數狀況是因為型別不一樣回傳得 false。

以下是 Kyle Simpson 的使用習慣

這部分其實是 Kyle Simpson 的論證,也是一種看待 JavaScript 不同的 Mindset。
我「個人偏好」說,這是洗腦時間,所以我把文字都保留下來。


在所有可能的地方,你都應該首選 == 。

瞭解型別總比不瞭解它們更好。
程式碼的不確定性是使程式碼難以閱讀的原因,它使程式碼容易受到意外的錯誤的影響。

有些人說 因為我不知道型別,所以我需要使用靜態型別,例如TypeScript等。

但是,== 並不是要與未知型別進行比較,這不是它的用途。
相反的,他要限定 null, undefined, string, number, boolean。

這個和很多人的認知相反。
大多數人,當他們不知道型別時,會使用==,然後陷入錯誤中。
所以,當您不知道型別時,切勿使用==。僅在知道型別時才使用==。

== 適用於知道型別,且要使用型別轉換(Coercion) 。
(我個人稱 抽象等價性 (==)


這個 TypeScript 提示其實是 JavaScript 本來就有的規則。
也是前面一直強調 嚴格相等( === ) 型別不同一定回傳 false
但這個提示如果你很熟悉 JavaScript 規則,其實沒必要,

因為有些時候 「你故意想比對型別,然後才比對」。
(但多數人都使用 typeof 去比對,沒有好好使用工具。)


如果您知道比較中的型別,並且它們有所不同,
據說 使用===可以保護您並保護您,但不同的 type 一定會回傳 false (像是 TS 提示一樣,broken)。

這邊主要是說,如果你要用多個 嚴格相等( === ) 達到 抽象等價性 (==) 的目的,
其實多個 嚴格相等( === ) 再做 && 運算子 會比 抽象等價性 (==) 的內部 Coercion 還要慢 (雖然這邊的慢是 microseconds 可以忽略)。

比如昨天的例子:


在大多數情況下,讀者沒有必要看到它們,
透過非常明顯的條列嚴格相等(===),並用 && 運算子連接,會分散他們的注意力。
而同樣的狀況之下,使用更正確的抽象等價性,即使用 == ,會使 code 更乾淨。
記住,如果您知道型別,這些敘述才是對的,只有在您知道型別的情況下,才提出這些聲明。


  1. 如果您不知道型別,則意味著您不完全瞭解程式碼,

這可能是不合理的要求。因為誰都無法保證100%知道整個系統中的型別,這是不合理的。

因此,當比較發生的時候,有時候存在一些不確定性。
Kyle Simpson 的主張是,那應該是一種少數狀況 (少數狀況使用 === ),而不是常態。

它的意思是,無論是少數狀況還是常態,由於不確定性(型別不確定)的緣故,這部分程式碼一定更難理解

所以乾脆在這個時候 重構(refactor) 你的 code


如果你真的不確定你的型別,就用 嚴格相等( === ) 直接告訴讀 code 的人。
因為 嚴格相等( === ) 是最容易表現不確定型別 的比較,且不會有 Corner Cases。

(個人註:但你最好補上註解,這時候寫註解是最恰當的,因為沒有型別的 code 真的很難理解。)

真的任何方法都不能確定型別,才使用 嚴格相等( === )。

大概解釋:
反正你能用 == 的狀況下(前面提的狀況),用 == 一定比較合適。
(通常是抽象或是要刻意做 Coercion 型別轉換。)

所以看到 === ,其實代表你不知道型別。

講那麼多,無論你是用 Kyle Simpson 的 mindset (使用 JavaScript 的 == 代表明確型別 ),
或是其他工具如 Flow, TypeScript ,你都還是要知道你寫的 code 的型別 ,否則都只能用 === (然後基本上都只回傳 false)。


上一篇
[day11] YDKJS (Equality) : 一般相等 == 比較「值」 , 嚴格相等 === 比較 「型別和值」是錯的?
下一篇
[day13] YDKJS (Scope) : JavaScript 是 parser language ? 不是 interpreter 嗎?
系列文
跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言